package com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.repository;

import cn.hutool.core.util.StrUtil;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.FlowNodeCandidateServiceAdapter;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.NodeCandidateService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.ResolveCandidateService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.UserRelationShipService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.ExpressionUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.StringUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ConstanceVariable;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowModelVariableVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowParticipantInfoVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.ModelCustomConfigDomain;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.ModelNextNodeCandidateSettings;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.userentity.UserInfo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowCandidateParticipantService;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowModelVariableRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowNextNodeCandidateRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowParticipantRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowReModelCustomConfigRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowUserTaskCustomExpressionRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowUserTaskExpressionResolveRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.UserRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.common.exception.BusiException;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.entities.FlowVarExpression;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.entities.FlowVarExpressionExample;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.mapper.FlowVarExpressionDAO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.common.api.FlowableException;
import org.flowable.engine.common.impl.el.ExpressionManager;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author wuliepeng
 */
@Slf4j
@Service
public class FlowUserTaskExpressionResolveRepositoryImpl implements FlowUserTaskExpressionResolveRepository {

    private transient final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private FlowParticipantRepository flowParticipantRepository;

    @Autowired
    private FlowVarExpressionDAO flowVarExpressionDAO;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private FlowModelVariableRepository flowModelVariableRepository;

    @Autowired
    private FlowCandidateParticipantService flowCandidateParticipantService;

    @Autowired
    private ResolveCandidateService resolveCandidateService;

    @Autowired
    private FlowNextNodeCandidateRepository flowNextNodeCandidateRepository;

    @Autowired
    private FlowNodeCandidateServiceAdapter flowNodeCandidateServiceAdapter;

    @Autowired
    private NodeCandidateService nodeCandidateService;

    @Autowired
    private FlowReModelCustomConfigRepository flowReModelCustomConfigRepository;

    @Override
    public String resolveAssignee(DelegateExecution execution) {
        List<FlowVarExpression> expressions = getExpressions(execution);
        ExpressionManager expressionManager = ExpressionUtils.getExpressionManager();

        String expressionValue = null;
        // 解析表达式并写入数据库
        if (CollectionUtils.isNotEmpty(expressions)) {
            String expression = expressions.get(0).getExpression();
            String expressionCode = ExpressionUtils.trimExpression(expression);
            String expressionType = getExpressionType(execution.getProcessDefinitionId(), expressionCode);
            Object value = expressionManager.createExpression(expression).getValue(execution);
            String label = null;
            if(value instanceof Map) {
                //{{"label", "超级管理员"}, {"value", "1711111111"}}
                expressionValue = ((Map<?, ?>) value).get("value").toString();
                label = ((Map<?, ?>) value).get("label").toString();
            } else if (value instanceof List) {
                List<Object> valueList = (List<Object>) value;
                expressionValue = valueList.stream().map(item -> ((Map<?,?>) item).get("value").toString()).collect(Collectors.joining());
            } else {
                expressionValue = value.toString();
            }

            if (StrUtil.isNotBlank(expressionValue)) {
                //根据解析出来的用户code到用户表里查找对应的名称
                expressionCode = flowCandidateParticipantService.resolveParticipantName(expressionValue, StrUtil.nullToDefault(label, expressionCode),expressionType);
            }
            flowParticipantRepository.insertParticipant(newFlowParticipantInfo(expressionValue, expressionType, expressionCode));
        }
        return expressionValue;
    }



    @Override
    public List<String> resolveCandidate(DelegateExecution execution) throws Exception{
        Set<String> expressionValues = new HashSet<>();

        // 判断是否重复处理标识
        Object hasAddIdEntityLink = execution.getTransientVariableLocal("hasAddIdEntityLink");
        if (Objects.nonNull(hasAddIdEntityLink) && hasAddIdEntityLink instanceof Boolean){
            return new ArrayList<>(expressionValues);
        }

        String parentExecutionId = null;
        String currentExecutionId = execution.getId();
        ExecutionEntity parentProcessInstanceExecution = nodeCandidateService.getSubProcessInstanceExecution(currentExecutionId);
        if (Objects.nonNull(parentProcessInstanceExecution)){
            parentExecutionId = parentProcessInstanceExecution.getId();
        }else{
            parentExecutionId = execution.getProcessInstanceId();
        }

        /**
         * 指定并设置流程后续节点候选人处理逻辑与未指定设置时,是互斥处理逻辑
         */
        List<ModelNextNodeCandidateSettings> candidateSettingsList = flowNextNodeCandidateRepository.selectByProcessInstanceIdAndNodeCodeAndParentExecutionId(execution.getProcessInstanceId()
                , execution.getCurrentActivityId(),parentExecutionId);
        if (CollectionUtils.isNotEmpty(candidateSettingsList)){

            // 先检查并清除流程任务节点配置的user和group的IdentityLinks数据
            // 该方法只针对指定设置了后续节点处理人时,需要清除,没有设置,则不需要走该方法逻辑
            this.cleanIdentityLinksByUserTask(execution);

            // 清除之后才将指定设置的候选人列表数据addTaskIdentityLink
            for (ModelNextNodeCandidateSettings modelNextNodeCandidateSettings : candidateSettingsList) {
                String type = modelNextNodeCandidateSettings.getDataType();
                String value = modelNextNodeCandidateSettings.getCode();
                String name = modelNextNodeCandidateSettings.getName();
                String processInstanceId = execution.getProcessInstanceId();
                // 流程待办时,指定候选节点候选人时保存的任务ID
                String taskId = modelNextNodeCandidateSettings.getTaskId();
                // 特殊处理关系或自定义函数类型
                if(Objects.equals(type, "RELATIONSHIP") ||
                        Objects.equals(type, "FUNCTION")) {
                    // 解析候选人列表
                    Set<UserInfo> userInfoSet = flowNodeCandidateServiceAdapter.handleNextNodeCandidateUsers(type, value, processInstanceId, taskId, execution.getCurrentActivityId());
                    // 给每个人员创建任务实例
                    this.addCandidateUsersByRsOrFunc(userInfoSet, execution, expressionValues);
                } else {
                    addTaskIdentityLink(type, value, execution, expressionValues);

                    addToParticipantInfo(value,type,name);
                }
            }
        }else{

            String deployId = String.valueOf(execution.getVariable(ConstanceVariable.DEPLOYMENT_ID));
            List<ModelCustomConfigDomain> modelCustomConfigDomainList = flowReModelCustomConfigRepository.listModelConfigByDeployIdAndType(deployId,execution.getCurrentActivityId(),ConstanceVariable.CANDIDATE_SET_NULL);
            if (CollectionUtils.isNotEmpty(modelCustomConfigDomainList)){
                if (Objects.equals(modelCustomConfigDomainList.get(0).getConfig(),"1")){
                    throw new Exception("当前节点["+execution.getCurrentActivityId()+"]处理人选择留空，并且未指定处理人，流程无法流转，请指定后重试");
                }
            }
            /**
             * 与上面逻辑是互斥逻辑
             */
            List<FlowVarExpression> expressions = getExpressions(execution);
            ExpressionManager expressionManager = ExpressionUtils.getExpressionManager();

            // 存量的流程，解析之后返回
            // 新编辑部署的流程，返回的数据为空，在 addTaskIdentityLink 方法中有处理
            if (CollectionUtils.isNotEmpty(expressions)) {
                for (FlowVarExpression flowVarExpression : expressions) {
                    // 数据类型,关系或自定义函数
                    String type = flowVarExpression.getType();
                    // 表达式数值,例如是${0},数值就是0
                    String expressionVal = flowVarExpression.getExpression();
                    // 流程实例ID
                    String processInstanceId = execution.getProcessInstanceId();
                    if (Objects.equals(type, "RELATIONSHIP") ||
                            Objects.equals(type, "FUNCTION")) {
                        // 解析候选人列表
                        Set<UserInfo> userInfoSet = flowNodeCandidateServiceAdapter.handleNextNodeCandidateUsers(type, expressionVal, processInstanceId, null, execution.getCurrentActivityId());
                        // 给每个人员创建任务实例
                        this.addCandidateUsersByRsOrFunc(userInfoSet, execution, expressionValues);
                    } else {
                        Object expressionValue = expressionManager.createExpression(flowVarExpression.getExpression()).getValue(execution);
                        if (expressionValue instanceof String) {
                            String[] valueStr = String.valueOf(expressionValue).split(",");
                            for (String str : valueStr) {
                                //addToList(expressionValues,type,str);
                                addTaskIdentityLink(type, str, execution, expressionValues);
                            }
                        } else if (expressionValue instanceof List) {
                            List valueList = (List) expressionValue;
                            for (Object obj : valueList) {
                                if (obj instanceof String) {
                                    //addToList(expressionValues,type,String.valueOf(obj));
                                    addTaskIdentityLink(type, String.valueOf(obj), execution, expressionValues);
                                }
                                if (obj instanceof Map) {
                                    Map objMap = (Map) obj;
                                    String value = String.valueOf(objMap.get("value"));
                                    if (org.apache.commons.lang3.StringUtils.isNotBlank(value)) {
                                        //addToList(expressionValues,type,value);
                                        addTaskIdentityLink(type, value, execution, expressionValues);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        execution.setTransientVariableLocal("hasAddIdEntityLink", true);
        return new ArrayList<>(expressionValues);
    }

    private void addToParticipantInfo(String value, String type, String name) {
        try {
            FlowParticipantInfoVo flowParticipantInfo = newFlowParticipantInfo(value,type,name);
            flowParticipantRepository.insertParticipant(flowParticipantInfo);
        } catch (Exception e) {
            logger.error("save participantInfo error,continue");
        }
    }

    private void addToList(List<String> expressionValues, String type, String value) {
        List<UserInfo> userInfoList =  resolveCandidateService.getUserInfoByCode(type,value, null);
        for (UserInfo userInfo : userInfoList) {
            expressionValues.add(userInfo.getUserCode());
        }
    }

    //如果type为空，则为存量数据，直接放入expressionValues返回
    private void addTaskIdentityLink(String type, String value, DelegateExecution execution,Set<String> expressionValues) {
        if (StringUtils.isBlank(value)){
            logger.error("IdentityLink value is null");
            return;
        }

        //存量流程的type为空
        if (StringUtils.isBlank(type)){
            expressionValues.add(value);
            return;
        }

        if (execution instanceof ExecutionEntity){
            ExecutionEntity executionEntity = (ExecutionEntity) execution;
            List<TaskEntity> taskList = executionEntity.getTasks();
            if (CollectionUtils.isNotEmpty(taskList)){
                if (Objects.equals("STA",type)){
                    taskList.get(0).addUserIdentityLink(value,type);
                }else{
                    taskList.get(0).addGroupIdentityLink(value,type);
                }
            }
        }
    }

    private void resolveStaUser(List<String> expressionValues, Object expressionValue) {
        if (expressionValue instanceof String){
            String staValueStr = String.valueOf(expressionValue);
            String[] staValues = staValueStr.split(",");
            for (String staValue : staValues) {
                expressionValues.add(staValue);
            }
        }else if (expressionValue instanceof List){
            List staList = (List) expressionValue;
            for (Object staObj : staList) {
                if (staObj instanceof String){
                    expressionValues.add(String.valueOf(staObj));
                }else if (staObj instanceof Map){
                    Map staMap = (Map) staObj;
                    String value = (String) staMap.get("value");
                    expressionValues.add(value);
                }
            }
        }
    }


    /**
     * 构建新的FlowParticipantInfo对象
     *
     * @param participantId
     * @param participantType
     * @param participantName
     * @return
     */
    private FlowParticipantInfoVo newFlowParticipantInfo(String participantId, String participantType, String participantName) {
        FlowParticipantInfoVo flowParticipantInfo = new FlowParticipantInfoVo();
        flowParticipantInfo.setParticipantId(participantId);
        flowParticipantInfo.setParticipantType(participantType);
        flowParticipantInfo.setParticipantName(participantName);
        flowParticipantInfo.setCreateTime(new Date());
        flowParticipantInfo.setUpdateTime(new Date());
        return flowParticipantInfo;
    }

    /**
     * 获取表达式列表
     *
     * @param execution
     * @return
     */
    private List<FlowVarExpression> getExpressions(DelegateExecution execution) {
        // 获取deployId
        List<FlowVarExpression> flowVarExpressions = new ArrayList<>();
        String processDefinitionId = execution.getProcessDefinitionId();
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
        if (processDefinition == null) {
            return flowVarExpressions;
        }

        // 获取表达式
        String deploymentId = processDefinition.getDeploymentId();
        String activityId = execution.getCurrentActivityId();
        FlowVarExpressionExample flowVarExpressionExample = new FlowVarExpressionExample();
        flowVarExpressionExample.createCriteria().andActivityIdEqualTo(activityId).andDeployIdEqualTo(deploymentId);
        flowVarExpressions = flowVarExpressionDAO.selectByExample(flowVarExpressionExample);
        return flowVarExpressions;
    }

    /**
     * 获取表达式变量类型
     *
     * @param processDefinitionId
     * @param expression
     * @return
     */
    private String getExpressionType(String processDefinitionId, String expression) {
        if (StringUtils.isNotEmpty(expression)) {
            ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
            String deploymentId = processDefinition.getDeploymentId();
            List<FlowModelVariableVo> modelVariables = flowModelVariableRepository.getModelVariables(deploymentId);
            for (FlowModelVariableVo flowModelVariable : modelVariables) {
                if (expression.equals(flowModelVariable.getCode())) {
                    return flowModelVariable.getType();
                }
            }
        }
        return "STA";
    }


    public List<String> getExpressResult(Object expressValue,DelegateExecution delegateExecution,String expression) {
        List<String> candidateInfo = new ArrayList<>();
        if (Objects.nonNull(expressValue)){
            if (expressValue instanceof String){
                String value = String.valueOf(expressValue);
                String[] values = value.split(",");
                for (String str : values) {
                    insertParticipant(delegateExecution.getProcessDefinitionId(), expression, str);
                    candidateInfo.add(str);
                }
            }else if (expressValue instanceof List){
                List list = (List)expressValue;
                list.stream().forEach(item -> {
                    if (item instanceof Map){
                        Map param = (Map) item;
                        String value = String.valueOf(param.get("value"));
                        insertParticipant(delegateExecution.getProcessDefinitionId(), expression, value);
                        candidateInfo.add(value);
                    }else if (item instanceof String){
                        String value = String.valueOf(item);
                        insertParticipant(delegateExecution.getProcessDefinitionId(), expression, value);
                        candidateInfo.add(value);
                    }
                });
            }else if (expressValue instanceof Map){
                Map param = (Map) expressValue;
                String value = String.valueOf(param.get("value"));
                candidateInfo.add(value);
            }else{
                logger.error("不持支的格式，请传入Map，List，或String");
            }
        }else{
            logger.error("表单式解析失败，请传入表达式的参数");
        }

        return candidateInfo;
    }


    /**
     * 存储参与者信息
     *
     * @param processDefinitionId
     * @param expression
     * @param value
     */
    private void insertParticipant(String processDefinitionId, String expression, String value) {
        flowCandidateParticipantService.saveParticipant(processDefinitionId,expression,value);
    }

    /**
     * 添加候选类型是关系或自定义函数类型对应的人员信息列表
     *
     * @param userInfoSet
     * @param execution
     * @param expressionValues
     */
    public void addCandidateUsersByRsOrFunc(Set<UserInfo> userInfoSet, DelegateExecution execution, Set<String> expressionValues) {
        if(CollectionUtils.isEmpty(userInfoSet)) {
            return;
        }

        // 给每个人添加任务
        for (UserInfo userInfo : userInfoSet) {
            addTaskIdentityLink("STA", userInfo.getUserCode(), execution, expressionValues);
            addToParticipantInfo(userInfo.getUserCode(),"STA",userInfo.getUserName());
        }
    }

    /**
     * 检查并清除流程任务节点配置的user和group的IdentityLinks数据
     * @param execution
     */
    private void cleanIdentityLinksByUserTask(DelegateExecution execution) {
        if (execution instanceof ExecutionEntity){
            ExecutionEntity executionEntity = (ExecutionEntity) execution;
            List<TaskEntity> taskList = executionEntity.getTasks();
            if (CollectionUtils.isNotEmpty(taskList)){
                FlowElement flowElement= executionEntity.getCurrentFlowElement();
                if (flowElement instanceof UserTask){
                    UserTask userTask = (UserTask) flowElement;
                    // 1、清除模板中配置的候选人数据
                    // 2、如果不清除的情况下,flowable在taskCreate完成时会将流程配置模板中配置候选人的内存数据加载到当前task中,会导致候选人数据设置无效
                    userTask.setCustomUserIdentityLinks(null);
                    userTask.setCustomGroupIdentityLinks(null);
                }
            }
        }
    }

}
